# 创建文件: tools/precise_concurrency_test.py
import requests
import time
import threading
from concurrent.futures import ThreadPoolExecutor
import statistics
import json


class PreciseConcurrencyTester:
    def __init__(self, base_url="http://127.0.0.1:8000"):
        self.base_url = base_url
        self.results = []

    def single_request(self, url, request_id):
        """单个请求测试"""
        start_time = time.time()
        try:
            response = requests.get(f"{self.base_url}{url}", timeout=10)
            end_time = time.time()
            return {
                'request_id': request_id,
                'status_code': response.status_code,
                'response_time': end_time - start_time,
                'success': response.status_code == 200,
                'timestamp': start_time
            }
        except Exception as e:
            end_time = time.time()
            return {
                'request_id': request_id,
                'status_code': 0,
                'response_time': end_time - start_time,
                'success': False,
                'error': str(e),
                'timestamp': start_time
            }

    def precise_test(self, url, concurrent_users, total_requests):
        """精确并发测试"""
        print(f"\n�� 精确测试: {url}")
        print(f"📊 并发用户: {concurrent_users}")
        print(f"�� 总请求数: {total_requests}")

        self.results = []
        start_time = time.time()

        # 使用信号量控制并发数
        semaphore = threading.Semaphore(concurrent_users)

        def limited_request(request_id):
            with semaphore:
                return self.single_request(url, request_id)

        with ThreadPoolExecutor(max_workers=concurrent_users * 2) as executor:
            futures = [executor.submit(limited_request, i) for i in range(total_requests)]
            self.results = [future.result() for future in futures]

        end_time = time.time()
        duration = end_time - start_time

        return self.analyze_precise_results(duration, concurrent_users)

    def analyze_precise_results(self, duration, concurrent_users):
        """分析精确测试结果"""
        total_requests = len(self.results)
        successful_requests = sum(1 for r in self.results if r['success'])
        failed_requests = total_requests - successful_requests
        response_times = [r['response_time'] for r in self.results if r['success']]

        success_rate = successful_requests / total_requests
        qps = total_requests / duration
        avg_response_time = statistics.mean(response_times) if response_times else 0

        print(f"\n📊 精确测试结果:")
        print(f"⏱️  测试时长: {duration:.3f}秒")
        print(f"�� 总请求数: {total_requests}")
        print(f"✅ 成功请求: {successful_requests}")
        print(f"❌ 失败请求: {failed_requests}")
        print(f"🎯 成功率: {success_rate * 100:.2f}%")
        print(f"🚀 QPS: {qps:.2f}")

        if response_times:
            print(f"⏱️  响应时间:")
            print(f"   平均: {avg_response_time:.3f}秒")
            print(f"   最小: {min(response_times):.3f}秒")
            print(f"   最大: {max(response_times):.3f}秒")
            print(f"   中位数: {statistics.median(response_times):.3f}秒")

        return {
            'concurrent_users': concurrent_users,
            'success_rate': success_rate,
            'avg_response_time': avg_response_time,
            'qps': qps,
            'total_requests': total_requests,
            'duration': duration
        }


def binary_search_max_concurrency():
    """使用二分查找找出最大并发量"""
    print("🔍 使用二分查找寻找最大并发量...")
    print("=" * 60)

    tester = PreciseConcurrencyTester()
    url = "/index/"

    # 二分查找范围
    left = 10
    right = 1000
    best_concurrency = 0
    best_result = None

    while left <= right:
        mid = (left + right) // 2
        print(f"\n�� 测试并发数: {mid}")

        result = tester.precise_test(url, mid, mid * 10)  # 每个并发数测试10倍请求

        if result['success_rate'] >= 0.95:  # 成功率>=95%认为可接受
            best_concurrency = mid
            best_result = result
            left = mid + 1
            print(f"✅ 成功率 {result['success_rate'] * 100:.1f}% - 可以尝试更高并发")
        else:
            right = mid - 1
            print(f"❌ 成功率 {result['success_rate'] * 100:.1f}% - 降低并发数")

        time.sleep(2)  # 测试间隔

    if best_result:
        print(f"\n🏆 找到最大安全并发数: {best_concurrency}")
        print(f"�� 最佳性能指标:")
        print(f"   成功率: {best_result['success_rate'] * 100:.2f}%")
        print(f"   平均响应时间: {best_result['avg_response_time']:.3f}秒")
        print(f"   QPS: {best_result['qps']:.2f}")

        return best_concurrency
    else:
        print("❌ 未找到合适的并发数")
        return 0


def main():
    print("�� Django项目精确并发量测试")
    print("=" * 60)

    # 检查服务器连接
    try:
        response = requests.get("http://127.0.0.1:8000/", timeout=5)
        print(f"✅ 服务器连接成功，状态码: {response.status_code}")
    except Exception as e:
        print(f"❌ 服务器连接失败: {e}")
        return

    # 选择测试方法
    print("\n请选择测试方法:")
    print("1. 二分查找法 (推荐) - 精确找到最大并发数")
    print("2. 递增测试法 - 逐步增加并发数")

    choice = input("请输入选择 (1/2): ")

    if choice == "1":
        max_concurrency = binary_search_max_concurrency()
    elif choice == "2":
        # 递增测试
        test_scenarios = [
            (50, 500, "50并发"),
            (100, 1000, "100并发"),
            (200, 2000, "200并发"),
            (300, 3000, "300并发"),
            (500, 5000, "500并发"),
        ]

        tester = PreciseConcurrencyTester()
        results = []

        for concurrent, total, desc in test_scenarios:
            print(f"\n{'=' * 60}")
            print(f"🧪 测试场景: {desc}")
            result = tester.precise_test("/index/", concurrent, total)
            results.append(result)

            if result['success_rate'] < 0.90:
                print(f"⚠️  成功率低于90%，停止测试")
                break

            time.sleep(2)

        # 找出最大并发数
        max_concurrency = 0
        for result in results:
            if result['success_rate'] >= 0.95:
                max_concurrency = result['concurrent_users']

        print(f"\n🏆 最大安全并发数: {max_concurrency}")
    else:
        print("无效选择")
        return

    print(f"\n�� 测试完成！")
    print(f"💡 建议生产环境使用并发数: {int(max_concurrency * 0.8)}")


if __name__ == "__main__":
    main()